#pragma once

#include "Vertex.h"
#include <dae.h>
#include <dom.h>
#include <dom/domCOLLADA.h>
#ifdef _DEBUG
#pragma comment(lib, "libcollada14dom22-d.lib")
#else
#pragma comment(lib, "libcollada14dom22.lib")
#endif
#pragma comment(lib, "libxml2_a.lib")
#pragma comment(lib, "zlib.lib")
#pragma comment(lib, "wsock32.lib")
#pragma comment(lib, "pcre-d.lib")
#pragma comment(lib, "pcrecpp-d.lib")
#pragma comment(lib, "minizip-d.lib")
#pragma comment(lib, "libboost_filesystem-d.lib")
#pragma comment(lib, "libboost_system-d.lib")
#include <string>
#include <vector>
using namespace std;

#include "..\\..\\Utility\\D3D\\D3DUtilityFunction.h"
#include "..\\MeshRender.h"



class Mesh : public CMeshRender
{
private:
	#pragma region //COLLADA members
	//<geometry> element to get mesh data from
	daeElement* geometry;

	//Component Vertex Data, to be compiled into Vertices later...
	vector<D3DXVECTOR3> Positions;
	vector<D3DXVECTOR2> UVs;
	vector<D3DXVECTOR3> Normals;
	vector<D3DXVECTOR3> Tangents;
	vector<D3DXVECTOR3> BiNormals;

	//Vertex type;
	enum VERTEXTYPE m_iVType;

	//Set it so COLLADALoader can access privates
	friend class COLLADALoader;

	//Combine the component vertex data to Vertices array
	void combineComponents()
	{
		for(unsigned int i = 0; i < Positions.size(); i++)
		{
			switch (m_iVType)
			{
			case (VERTEXTYPE::VT_POS):
				Vertices.push_back(new Vertex(Positions[i]));
				break;
			case (VERTEXTYPE::VT_POSNORM):
				Vertices.push_back(new VertexNorm(Positions[i],Normals[i]));
				break;
			case (VERTEXTYPE::VT_POSNORMUV):
				Vertices.push_back(new VertexNormUV(Positions[i],Normals[i],UVs[i]));
				break;
			case (VERTEXTYPE::VT_POSNORMUVTANG):
				Vertices.push_back(new VertexNormUVTang(Positions[i],Normals[i],UVs[i],Tangents[i],BiNormals[i]));
				break;
			}				
		}
	}
	#pragma endregion
	//Stride size
	int STRIDE_SIZE;
	//Set access rights
	friend class MorphingMesh;
public:
	//Name of this mesh
	string Name;
	//World transform
	D3DXMATRIX World;
	//Combined Vertex Data, ready for Vertex Buffer
	vector<Vertex*> Vertices;
	//Index data, ready for Index Buffer
	vector<unsigned int> Indices;

	//Constructor
	Mesh(string Name, D3DXMATRIX World)
	{
		//Set Name
		this->Name = Name;
		
		//Set World
		this->World = World;

		//Set Vertex Type
		m_iVType = VERTEXTYPE::VT_POSNORMUV;
		STRIDE_SIZE = VertexNormUV::m_iDataSize;
		printf("The inititial VType of the mesh is: VERTEXTYPE::VT_POSNORMUV, please correct the vertex type before using it!!!\n");

		//Initialize Component Vertex Data arrays
		Positions = vector<D3DXVECTOR3>();
		UVs = vector<D3DXVECTOR2>();
		Normals = vector<D3DXVECTOR3>();
		Tangents = vector<D3DXVECTOR3>();
		BiNormals = vector<D3DXVECTOR3>();

		//Initialize Combined Vertex Data array
		Vertices = vector<Vertex*>();
		//Initialize Index Data Array
		Indices = vector<unsigned int>();
		//Initialize COLLADA pointers to NULL
		geometry = NULL;

	}

	void SetVType(enum VERTEXTYPE VType)
	{
		//Set Vertex Type
		m_iVType = VType;
		printf("The VType of this mesh is: %d\n", m_iVType);
		switch (m_iVType)
		{
		case (VERTEXTYPE::VT_POS):
			STRIDE_SIZE = Vertex::m_iDataSize;
			break;
		case (VERTEXTYPE::VT_POSNORM):
			STRIDE_SIZE = VertexNorm::m_iDataSize;
			break;
		case (VERTEXTYPE::VT_POSNORMUV):
			STRIDE_SIZE = VertexNormUV::m_iDataSize;
			break;
		case (VERTEXTYPE::VT_POSNORMUVTANG):
			STRIDE_SIZE = VertexNormUVTang::m_iDataSize;
			break;
		}
		printf("The current VType of the mesh is: %d\n", m_iVType);
	}

	//Destructor
	virtual ~Mesh()
	{
		for(int i = 0; i < Vertices.size(); i++)
			SAFE_DELETE(Vertices[i]);
		Vertices.clear();

		OnD3D11DestroyDevice(NULL);
	}

	//OnDeviceReset
	virtual HRESULT OnD3D11CreateDevice( ID3D11Device* pd3dDevice, ID3DX11EffectTechnique* pTech,
		const DXGI_SURFACE_DESC* pBackBufferSurfaceDesc, CDirectoryUtil* pDirUtil, void* pUserContext)
	{
		HRESULT hr = S_OK;

		//Get vertex buffer size
		if (Vertices.size() == 0)
		{
			printf("No geometry data loaded yet!\n");
			OUTPUTERRORINFO;
			return E_FAIL;
		}
		//Create vertex layout
		switch (m_iVType)
		{
		case (VERTEXTYPE::VT_POS):
			V_RETURN(CD3DUtilityFunction::CreateVertexLayout(pd3dDevice,Vertex::layout,ARRAYSIZE(Vertex::layout),pTech,&m_pVertexLayout));
			break;
		case (VERTEXTYPE::VT_POSNORM):
			V_RETURN(CD3DUtilityFunction::CreateVertexLayout(pd3dDevice,VertexNorm::layout,ARRAYSIZE(VertexNorm::layout),pTech,&m_pVertexLayout));
			break;
		case (VERTEXTYPE::VT_POSNORMUV):
			V_RETURN(CD3DUtilityFunction::CreateVertexLayout(pd3dDevice,VertexNormUV::layout,ARRAYSIZE(VertexNormUV::layout),pTech,&m_pVertexLayout));
			break;
		case (VERTEXTYPE::VT_POSNORMUVTANG):
			V_RETURN(CD3DUtilityFunction::CreateVertexLayout(pd3dDevice,VertexNormUVTang::layout,ARRAYSIZE(VertexNormUVTang::layout),pTech,&m_pVertexLayout));
			break;
		}
		
		//Create VertexBuffer
		V_RETURN(CD3DUtilityFunction::CreateVertexBuffer(pd3dDevice,STRIDE_SIZE, (float*)(Vertices.data()), Vertices.size(), &m_pVB));
		//Create Index Buffer
		V_RETURN(CD3DUtilityFunction::CreateIndexBuffer(pd3dDevice,Indices.size(),Indices.data(),&m_pIB));
}

	//Draw
	void Render(ID3D11Device* pd3dDevice, ID3D11DeviceContext* pd3dImmediateContext, ID3DX11Effect* pEffect, ID3DX11EffectTechnique* pRenderTech,
		CBaseCamera* pViewCamera, CDirectoryUtil* pDirUtil, double fTime, float fElapsedTime, void* pUserContext)
	{
		HRESULT hr = S_OK;
		// Get the projection & view matrix from the camera class
		CModelViewerCamera* pCurCamera = static_cast<CModelViewerCamera*>(pViewCamera);
		D3DXMATRIX mWorld = *(pCurCamera->GetWorldMatrix());
		D3DXMATRIX mView = *(pCurCamera->GetViewMatrix());
		D3DXMATRIX mProj = *(pCurCamera->GetProjMatrix());
		D3DXMATRIX mWorldView = mWorld * mView;
		D3DXMATRIX mWorldViewProjection = mWorldView * mProj;
		hr = (pEffect->GetVariableByName("mWorld")->AsMatrix())->SetMatrix((float*)(&mWorld));
		hr = (pEffect->GetVariableByName("mWorldView")->AsMatrix())->SetMatrix((float*)(&mWorldView));
		hr = (pEffect->GetVariableByName("mWorldViewProjection")->AsMatrix())->SetMatrix((float*)(&mWorldViewProjection));
		// Set render resources
		pd3dImmediateContext->IASetInputLayout( m_pVertexLayout );
		ID3D11Buffer* pVB[1] = { m_pVB };
		UINT pStride[1] = {STRIDE_SIZE};
		UINT pOffset[1] = { 0 };
		pd3dImmediateContext->IASetVertexBuffers( 0, 1, pVB, pStride, pOffset );
		pd3dImmediateContext->IASetIndexBuffer(m_pIB, DXGI_FORMAT_R32_UINT, 0);
		pd3dImmediateContext->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

		// Render objects here...
		// Apply the technique contained in the effect 
		D3DX11_TECHNIQUE_DESC Desc;
		pRenderTech->GetDesc( &Desc );

		for( UINT iPass = 0; iPass < Desc.Passes; iPass++ )
		{
			pRenderTech->GetPassByIndex( iPass )->Apply( 0, pd3dImmediateContext );
			pd3dImmediateContext->DrawIndexed( Indices.size(), 0, 0 );			
		}
	}
};
